using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Audio;

using HadronData;

using Hadron.Entities;
using Hadron.SpecialEffects;

namespace Hadron
{
    enum PlayerShipStatus
    {
        WarpingIn,
        Active,
        Exploding,
        WarpingOut,
        WarpedOut
    }

    enum RotationThruster
    {
        Neutral,
        Clockwise,
        CounterClockwise
    }

    class PlayerShip
    {
        private PlayerShipStatus m_playerShipStatus;
        private double m_dTime;
        private Environment m_environment;
        private Transformation m_trnPlacement;
        private Vector2 m_vecVelocity;
        private bool m_bLandingGear;
        private ParticleSystem m_particleSystem;
        private Polygon m_polygonHullLocal, m_polygonCockpitLocal, m_polygonHullWorld, m_polygonCockpitWorld, m_polygonLandingGear;
        private Circle m_circleShield;
        private float m_fStateTime;
        private float m_fShieldAngle;
        private float m_fShieldState;
        private float m_fLandingGearState;
        private float m_fIntegrity;
        private float m_fFuel;
        private float m_fFireReload;
        private int m_nCratesDelivered, m_nScientistsRescued, m_nReactorsDestroyed;
        private bool m_bForwardThruster, m_bShield, m_bFire, m_bTractorBeam;
        private RotationThruster m_rotationThruster;
        private SoundEffectInstance m_soundEffectInstanceJet;
        private SoundEffectInstance m_soundEffectInstanceShield;
        private SoundEffectInstance m_soundEffectInstanceTractorBeam;

        private void ResetLife()
        {
            m_vecVelocity = Vector2.Zero;
            m_bLandingGear = false;
            m_fShieldState = 0.0f;
            m_fLandingGearState = 0.0f;
            m_fIntegrity = 100.0f;
            m_fFuel = 100.0f;
            m_fFireReload = 0.0f;

            m_trnPlacement.Rotation = -MathHelper.PiOver2;
            m_particleSystem.Active = false;

            m_bForwardThruster = false;
            m_rotationThruster = RotationThruster.Neutral;
            m_bShield = false;
            m_bFire = false;
            m_bTractorBeam = false;
        }

        private void ResetLevel()
        {
            m_nCratesDelivered = 0;
            m_nScientistsRescued = 0;
            m_nReactorsDestroyed = 0;
            ResetLife();
        }

        public PlayerShip(Environment p_environment)
        {
            m_playerShipStatus = PlayerShipStatus.WarpingIn;
            m_fStateTime = 1.0f;
            m_environment = p_environment;
            m_trnPlacement = Transformation.Identity;

            m_particleSystem = new ParticleSystem(800, 200.0f, 4.0f);
            m_particleSystem.EmissionVelocityVariance = 16.0f;

            Color[] arrayThrusterColours = new Color[5];
            arrayThrusterColours[0] = new Color(255, 192, 32);
            arrayThrusterColours[1] = Color.Orange;
            arrayThrusterColours[2] = Color.Firebrick;
            arrayThrusterColours[3] = new Color(64, 0, 32);
            arrayThrusterColours[4] = Color.Black;
            m_particleSystem.ParticleColours = arrayThrusterColours;

            // collision polygon
            Vector2[] arrayVertices = new Vector2[11];
            arrayVertices[ 0] = new Vector2(-14.0f,  -3.0f);
            arrayVertices[ 1] = new Vector2(-11.0f,  -4.0f);
            arrayVertices[ 2] = new Vector2( -9.0f, -13.0f);
            arrayVertices[ 3] = new Vector2( -4.0f, -13.0f);
            arrayVertices[ 4] = new Vector2(  2.0f,  -6.0f);
            arrayVertices[ 5] = new Vector2( 14.0f,   0.0f);
            arrayVertices[ 6] = new Vector2(  2.0f,   6.0f);
            arrayVertices[ 7] = new Vector2( -4.0f,  13.0f);
            arrayVertices[ 8] = new Vector2( -9.0f,  13.0f);
            arrayVertices[ 9] = new Vector2(-11.0f,   4.0f);
            arrayVertices[10] = new Vector2(-14.0f,   3.0f);
            m_polygonHullLocal = new Polygon();
            m_polygonHullLocal.Vertices = arrayVertices;
            m_polygonHullLocal.Colour = Color.White;
            m_polygonHullLocal.Edges[0].Colour = Color.DarkOrange;
            m_polygonHullLocal.Edges[1].Colour = Color.Gray;
            m_polygonHullLocal.Edges[10].Colour = Color.Gray;

            m_polygonHullWorld = new Polygon(m_polygonHullLocal);

            // cockpit
            m_polygonCockpitLocal = new Polygon();
            arrayVertices = new Vector2[4];
            arrayVertices[0] = new Vector2(10.0f, 0.0f);
            arrayVertices[1] = new Vector2( 2.0f, 4.0f);
            arrayVertices[2] = new Vector2(0.0f, 0.0f);
            arrayVertices[3] = new Vector2(2.0f, -4.0f);
            m_polygonCockpitLocal.Vertices = arrayVertices;
            m_polygonCockpitLocal.Colour = new Color(0, 128, 255);
            m_polygonCockpitWorld = new Polygon(m_polygonCockpitLocal);

            // landing gear
            m_polygonLandingGear = new Polygon();
            for (int nIndex = 0; nIndex < 4; nIndex++)
                m_polygonLandingGear.Edges.Add(new Edge(Color.Gray));

            m_circleShield = new Circle(Position, 26.0f);

            ResetLevel();
        }

        public void Update(GameTime p_gameTime)
        {
            // handle play status
            m_dTime = p_gameTime.TotalGameTime.TotalSeconds;
            float fDeltaTime = (float)p_gameTime.ElapsedGameTime.TotalSeconds;

            m_fStateTime = Math.Max(0.0f, (m_fStateTime - fDeltaTime));

            switch (m_playerShipStatus)
            {
                case PlayerShipStatus.WarpingIn:
                    if (m_fStateTime == 0.0f)
                        m_playerShipStatus = PlayerShipStatus.Active;
                    break;
                case PlayerShipStatus.Active:
                    if (m_environment.LevelComplete)
                    {
                        m_fStateTime = 1.0f;
                        m_vecVelocity = Vector2.Zero;
                        m_playerShipStatus = PlayerShipStatus.WarpingOut;
                    }
                    else if (m_fIntegrity == 0.0f)
                    {
                        m_environment.SpecialEffects.Add(new Explosion(m_environment, Position, 2.0f, 3.0f));
                        ResetLife();
                        m_fStateTime = 2.0f;
                        m_playerShipStatus = PlayerShipStatus.Exploding;
                    }
                    break;
                case PlayerShipStatus.Exploding:
                    if (m_fStateTime == 0.0f)
                    {
                        Portal portal = m_environment.GetNearestEntryPortal(this);
                        Position = portal.Position;
                        portal.Open = true;
                        m_fStateTime = 1.0f;
                        --m_environment.Context.Lives;
                        m_playerShipStatus = PlayerShipStatus.WarpingIn;
                    }
                    break;
                case PlayerShipStatus.WarpingOut:
                    if (m_fStateTime == 0.0f)
                        m_playerShipStatus = PlayerShipStatus.WarpedOut;
                    break;
            }

            // turn off thruster and shield if no fuel or not active
            if (m_fFuel == 0.0f || m_playerShipStatus != PlayerShipStatus.Active)
            {
                m_bForwardThruster = false;
                m_bShield = false;
            }

            // forward thruster
            if (m_bForwardThruster)
            {
                if (m_soundEffectInstanceJet == null)
                    m_soundEffectInstanceJet
                        = AssetManager.Sounds.Jet.Play(1.0f, 0.0f, 0.0f, true);

                Vector2 vecAcceleration = m_trnPlacement.RotationDirection * 128.0f;
                m_vecVelocity += vecAcceleration * fDeltaTime;
                m_fFuel = Math.Max(0.0f, m_fFuel - fDeltaTime * 5.0f);
            }
            else
            {
                if (m_soundEffectInstanceJet != null)
                {
                    m_soundEffectInstanceJet.Stop();
                    m_soundEffectInstanceJet = null;
                }
            }

            // rotation thruster
            if (!m_bLandingGear)
            {
                switch (m_rotationThruster)
                {
                    case RotationThruster.Clockwise:
                        Orientation += MathHelper.TwoPi * fDeltaTime;
                        break;
                    case RotationThruster.CounterClockwise:
                        Orientation -= MathHelper.TwoPi * fDeltaTime;
                        break;
                }
            }

            if (m_bShield)
            {
                if (m_soundEffectInstanceShield == null)
                    m_soundEffectInstanceShield
                        = AssetManager.Sounds.Shield.Play(1.0f, 0.0f, 0.0f, true);

                m_fShieldState = Math.Min(1.0f, m_fShieldState + fDeltaTime * 8.0f);
                m_fFuel = Math.Max(0.0f, m_fFuel - fDeltaTime * 10.0f);
            }
            else
            {
                if (m_soundEffectInstanceShield != null)
                {
                    m_soundEffectInstanceShield.Stop();
                    m_soundEffectInstanceShield = null;
                }

                m_fShieldState = Math.Max(0.0f, m_fShieldState - fDeltaTime * 4.0f);
            }

            if (m_bLandingGear)
                m_fLandingGearState = Math.Min(1.0f, m_fLandingGearState + fDeltaTime * 4.0f);
            else
                m_fLandingGearState = Math.Max(0.0f, m_fLandingGearState - fDeltaTime * 2.0f);

            // integrity depletion if no fuel
            if (m_fFuel == 0.0f)
                m_fIntegrity = Math.Max(0.0f, m_fIntegrity - fDeltaTime * 10.0f); ;

            // firing
            m_fFireReload = Math.Max(0.0f, m_fFireReload - fDeltaTime);
            if (m_bFire && m_fFireReload == 0.0)
            {
                AssetManager.Sounds.Pulse.Play();
                PlayerProjectile playerProjectile = new PlayerProjectile(m_environment);
                playerProjectile.Position = Position + Direction * 8.0f;
                playerProjectile.Velocity = Velocity + Direction * 512.0f;
                playerProjectile.Angle = Orientation;
                playerProjectile.Lifetime = 2.0f;
                m_environment.PlayerProjectiles.Add(playerProjectile);
                m_fFireReload = 0.25f;
            }

            // gravity
            if (m_playerShipStatus == PlayerShipStatus.Active)
                m_vecVelocity += m_environment.Gravity * fDeltaTime;

            // integrate position
            m_trnPlacement.Translation += m_vecVelocity * fDeltaTime;

            // limit to level dimensions
            Level level = m_environment.CurrentLevel;
            Vector2 vecPosition = m_trnPlacement.Translation;
            if (vecPosition.X < 0.0f)
            {
                vecPosition.X = 0.0f;
                m_vecVelocity.X = Math.Abs(m_vecVelocity.X) * 0.5f;
            }
            if (vecPosition.Y < 0.0f)
            {
                vecPosition.Y = 0.0f;
                m_vecVelocity.Y = Math.Abs(m_vecVelocity.Y) * 0.5f;
            }
            if (vecPosition.X > level.Dimensions.X)
            {
                vecPosition.X = level.Dimensions.X;
                m_vecVelocity.X = -Math.Abs(m_vecVelocity.X) * 0.5f;
            }
            if (vecPosition.Y > level.Dimensions.Y)
            {
                vecPosition.Y = level.Dimensions.Y;
                m_vecVelocity.Y = -Math.Abs(m_vecVelocity.Y) * 0.5f;
            }
            m_trnPlacement.Translation = vecPosition;

            // update jet particle system
            m_particleSystem.Position = m_trnPlacement.Translation - m_trnPlacement.RotationDirection * 16.0f; ;
            m_particleSystem.EmissionVelocity = m_vecVelocity + m_trnPlacement.RotationDirection * -32.0f;
            m_particleSystem.Update(p_gameTime);
            m_particleSystem.Active = m_bForwardThruster;

            // update hull geometry
            m_polygonHullWorld.Transform(m_trnPlacement, m_polygonHullLocal);
            m_polygonCockpitWorld.Transform(m_trnPlacement, m_polygonCockpitLocal);

            // landing gear geometry
            Vector2 vecLegStart = new Vector2(-10.0f, -8.0f);
            Vector2 vecLegDelta = new Vector2(8.0f, 4.0f) * m_fLandingGearState;
            Vector2 vecLegEnd = vecLegStart - vecLegDelta;

            m_polygonLandingGear.Edges[0].Start = vecLegStart;
            m_polygonLandingGear.Edges[0].End = vecLegEnd;

            m_polygonLandingGear.Edges[1].Start = new Vector2(vecLegEnd.X, vecLegEnd.Y - 4.0f);
            m_polygonLandingGear.Edges[1].End = new Vector2(vecLegEnd.X, vecLegEnd.Y + 5.0f);

            vecLegStart.Y = -vecLegStart.Y;
            vecLegEnd.Y = -vecLegEnd.Y;
            m_polygonLandingGear.Edges[2].Start = vecLegStart;
            m_polygonLandingGear.Edges[2].End = vecLegEnd;

            m_polygonLandingGear.Edges[3].Start = new Vector2(vecLegEnd.X, vecLegEnd.Y - 4.0f);
            m_polygonLandingGear.Edges[3].End = new Vector2(vecLegEnd.X, vecLegEnd.Y + 5.0f);

            m_polygonLandingGear.Transform(m_trnPlacement);

            //shield
            m_fShieldAngle += fDeltaTime * MathHelper.Pi;
            m_circleShield.Centre = m_trnPlacement.Translation;
        }

        public void Draw(SpriteBatch p_spriteBatch)
        {
            if (m_playerShipStatus == PlayerShipStatus.WarpingOut
                || m_playerShipStatus == PlayerShipStatus.WarpedOut)
                return;

            if (m_playerShipStatus == PlayerShipStatus.WarpingIn)
            {
                float fOpacity = 1.0f - m_fStateTime;
                m_polygonHullWorld.Colour = new Color(Color.White, fOpacity);
                m_polygonCockpitWorld.Colour = new Color(0.0f, 0.5f, 1.0f, fOpacity);
            }
            else
            {
                m_polygonHullWorld.Colour = Color.White;
                m_polygonCockpitWorld.Colour = new Color(0, 128, 255);
            }

            if (m_playerShipStatus != PlayerShipStatus.Exploding)
            {
                // hull
                m_polygonHullWorld.Draw(p_spriteBatch);

                // cockpit
                m_polygonCockpitWorld.Draw(p_spriteBatch);

                // landing gear
                if (m_fLandingGearState > 0.0f)
                {
                    m_polygonLandingGear.Draw(p_spriteBatch);

                    // landing lights
                    float fLightAngle = 0.0f;
                    bool bLight = (DateTime.Now.Ticks / 10000000) % 2 == 0;
                    if (bLight)
                        p_spriteBatch.Draw(AssetManager.Textures.Light,
                            new Rectangle((int)Position.X - 14, (int)Position.Y + 4, 16, 16),
                            null, Color.LightGreen, fLightAngle, new Vector2(8.0f, 8.0f), SpriteEffects.None, 0.0f);
                    else
                        p_spriteBatch.Draw(AssetManager.Textures.Light,
                            new Rectangle((int)Position.X + 15, (int)Position.Y + 4, 16, 16),
                            null, Color.Red, fLightAngle, new Vector2(8.0f, 8.0f), SpriteEffects.None, 0.0f);
                }


                // shield
                Texture2D textureShield = AssetManager.Textures.Shield;
                Vector2 vecShieldOrigin = new Vector2(32.0f, 32.0f);
                p_spriteBatch.Draw(textureShield, Position, null, Color.White,
                    m_fShieldAngle * 0.1f,
                    vecShieldOrigin, m_fShieldState, SpriteEffects.None, 0.0f);
                p_spriteBatch.Draw(textureShield, Position, null, Color.Cyan,
                    -m_fShieldAngle * 0.2f,
                    vecShieldOrigin, m_fShieldState, SpriteEffects.None, 0.0f);
                p_spriteBatch.Draw(textureShield, Position, null, Color.LightBlue,
                    m_fShieldAngle * 0.3f,
                    vecShieldOrigin, m_fShieldState, SpriteEffects.None, 0.0f);
                p_spriteBatch.Draw(textureShield, Position, null, Color.Blue,
                    -m_fShieldAngle * 0.4f,
                    vecShieldOrigin, m_fShieldState, SpriteEffects.None, 0.0f);
            }

            // jet
            m_particleSystem.Draw(p_spriteBatch);            
        }

        public void Terminate()
        {
            if (m_soundEffectInstanceJet != null)
                m_soundEffectInstanceJet.Stop();
            if (m_soundEffectInstanceShield != null)
                m_soundEffectInstanceShield.Stop();
            if (m_soundEffectInstanceTractorBeam != null)
                m_soundEffectInstanceTractorBeam.Stop();
        }

        public PlayerShipStatus Status
        {
            get { return m_playerShipStatus; }
            set { m_playerShipStatus = value; }
        }

        public Transformation Placement
        {
            get { return m_trnPlacement; }
            set { m_trnPlacement = value; }
        }

        public Vector2 Position
        {
            get { return m_trnPlacement.Translation; }
            set { m_trnPlacement.Translation = value; }
        }

        public float Orientation
        {
            get { return m_trnPlacement.Rotation; }
            set { m_trnPlacement.Rotation = value; }
        }

        public Vector2 Velocity
        {
            get { return m_vecVelocity; }
            set { m_vecVelocity = value; }
        }

        public Vector2 Direction
        {
            get { return m_trnPlacement.RotationDirection; }
        }

        public bool ForwardThruster
        {
            get { return m_bForwardThruster; }
            set { m_bForwardThruster = value; }
        }

        public RotationThruster RotationThruster
        {
            get { return m_rotationThruster; }
            set { m_rotationThruster = value; }
        }

        public bool Shield
        {
            get { return m_bShield; }
            set { m_bShield = value; } 
        }

        public bool Fire
        {
            get { return m_bFire; }
            set { m_bFire = value; }
        }

        public bool LandingGear
        {
            get { return m_bLandingGear; }
            set
            {
                if (m_bLandingGear == value)
                    return;
                m_bLandingGear = value;
                if (m_bLandingGear)
                    AssetManager.Sounds.LandingOpen.Play();
                else
                    AssetManager.Sounds.LandingClose.Play();
            }
        }

        public bool TractorBeam
        {
            get { return m_bTractorBeam; }
            set
            {
                m_bTractorBeam = value;

                if (m_bTractorBeam)
                {
                    if (m_soundEffectInstanceTractorBeam == null)
                        m_soundEffectInstanceTractorBeam
                            = AssetManager.Sounds.TractorBeam.Play(1.0f, 0.0f, 0.0f, true);
                }
                else
                {
                    if (m_soundEffectInstanceTractorBeam != null)
                    {
                        m_soundEffectInstanceTractorBeam.Stop();
                        m_soundEffectInstanceTractorBeam = null;
                    }
                }
            }
        }

        public int CratesDelivered
        {
            get { return m_nCratesDelivered; }
            set { m_nCratesDelivered = value; }
        }

        public int ScientistsRescued
        {
            get { return m_nScientistsRescued; }
            set { m_nScientistsRescued = value; }
        }

        public int ReactorsDestroyed
        {
            get { return m_nReactorsDestroyed; }
            set { m_nReactorsDestroyed = value; }
        }

        public Circle BoundingCircle
        {
            get { return m_circleShield; }
        }

        public Geometry HullGeometry
        {
            get { return m_polygonHullWorld; }
        }

        public Geometry LandingGearGeometry
        {
            get { return m_polygonLandingGear; }
        }

        public Edge LandingSensor
        {
            get
            {
                Edge edge = new Edge(new Vector2(-32.0f, 0.0f), new Vector2(0.0f, 0.0f));
                edge.Transform(m_trnPlacement);
                return edge;
            }
        }

        public Circle ShieldGeometry
        {
            get { return m_circleShield; }
        }

        public float Integrity
        {
            get { return m_fIntegrity; }
            set { m_fIntegrity = Math.Max(0.0f, Math.Min(100.0f, value)); }
        }

        public float Fuel
        {
            get { return m_fFuel; }
            set { m_fFuel = Math.Max(0.0f, Math.Min(100.0f, value)); }
        }

        public bool Landed
        {
            get
            {
                return m_bLandingGear
                    && m_fLandingGearState == 1.0f
                    && m_vecVelocity.LengthSquared() < 4.0f
                    && m_bForwardThruster == false
                    && m_rotationThruster == RotationThruster.Neutral;
            }
        }
    }
}
